home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2009 February / PCWFEB09.iso / Software / Linux / Kubuntu 8.10 / kubuntu-8.10-desktop-i386.iso / casper / filesystem.squashfs / usr / share / ubiquity / install.py < prev    next >
Text File  |  2008-10-29  |  84KB  |  2,116 lines

  1. #!/usr/bin/python
  2. # -*- coding: utf-8 -*-
  3.  
  4. # Copyright (C) 2005 Javier Carranza and others for Guadalinex
  5. # Copyright (C) 2005, 2006 Canonical Ltd.
  6. # Copyright (C) 2007 Mario Limonciello
  7. #
  8. # This program is free software; you can redistribute it and/or modify
  9. # it under the terms of the GNU General Public License as published by
  10. # the Free Software Foundation; either version 2 of the License, or
  11. # (at your option) any later version.
  12. #
  13. # This program is distributed in the hope that it will be useful,
  14. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  16. # GNU General Public License for more details.
  17. #
  18. # You should have received a copy of the GNU General Public License
  19. # along with this program; if not, write to the Free Software
  20. # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  21.  
  22. import sys
  23. import os
  24. import platform
  25. import errno
  26. import stat
  27. import re
  28. import textwrap
  29. import shutil
  30. import subprocess
  31. import time
  32. import struct
  33. import socket
  34. import fcntl
  35. import traceback
  36. import syslog
  37. import gzip
  38. import debconf
  39. import warnings
  40. warnings.filterwarnings("ignore", "apt API not stable yet", FutureWarning)
  41. import apt_pkg
  42. from apt.cache import Cache
  43. from apt.progress import FetchProgress, InstallProgress
  44. from hashlib import md5
  45.  
  46. sys.path.insert(0, '/usr/lib/ubiquity')
  47.  
  48. from ubiquity import misc
  49. from ubiquity import osextras
  50. from ubiquity.components import language_apply, apt_setup, timezone_apply, \
  51.                                 clock_setup, console_setup_apply, \
  52.                                 usersetup_apply, hw_detect, check_kernels, \
  53.                                 migrationassistant_apply
  54.  
  55. def debconf_disconnect():
  56.     """Disconnect from debconf. This is only to be used as a subprocess
  57.     preexec_fn helper."""
  58.     os.environ['DEBIAN_FRONTEND'] = 'noninteractive'
  59.     if 'DEBIAN_HAS_FRONTEND' in os.environ:
  60.         del os.environ['DEBIAN_HAS_FRONTEND']
  61.     if 'DEBCONF_USE_CDEBCONF' in os.environ:
  62.         # Probably not a good idea to use this in /target too ...
  63.         del os.environ['DEBCONF_USE_CDEBCONF']
  64.  
  65. class DebconfFetchProgress(FetchProgress):
  66.     """An object that reports apt's fetching progress using debconf."""
  67.  
  68.     def __init__(self, db, title, info_starting, info):
  69.         FetchProgress.__init__(self)
  70.         self.db = db
  71.         self.title = title
  72.         self.info_starting = info_starting
  73.         self.info = info
  74.         self.old_capb = None
  75.         self.eta = 0.0
  76.  
  77.     def start(self):
  78.         self.db.progress('START', 0, 100, self.title)
  79.         if self.info_starting is not None:
  80.             self.db.progress('INFO', self.info_starting)
  81.         self.old_capb = self.db.capb()
  82.         capb_list = self.old_capb.split()
  83.         capb_list.append('progresscancel')
  84.         self.db.capb(' '.join(capb_list))
  85.  
  86.     # TODO cjwatson 2006-02-27: implement updateStatus
  87.  
  88.     def pulse(self):
  89.         FetchProgress.pulse(self)
  90.         try:
  91.             self.db.progress('SET', int(self.percent))
  92.         except debconf.DebconfError:
  93.             return False
  94.         if self.eta != 0.0:
  95.             time_str = "%d:%02d" % divmod(int(self.eta), 60)
  96.             self.db.subst(self.info, 'TIME', time_str)
  97.             try:
  98.                 self.db.progress('INFO', self.info)
  99.             except debconf.DebconfError:
  100.                 return False
  101.         return True
  102.  
  103.     def stop(self):
  104.         if self.old_capb is not None:
  105.             self.db.capb(self.old_capb)
  106.             self.old_capb = None
  107.             self.db.progress('STOP')
  108.  
  109. class DebconfInstallProgress(InstallProgress):
  110.     """An object that reports apt's installation progress using debconf."""
  111.  
  112.     def __init__(self, db, title, info, error=None):
  113.         InstallProgress.__init__(self)
  114.         self.db = db
  115.         self.title = title
  116.         self.info = info
  117.         self.error_template = error
  118.         self.started = False
  119.         # InstallProgress uses a non-blocking status fd; our run()
  120.         # implementation doesn't need that, and in fact we spin unless the
  121.         # fd is blocking.
  122.         flags = fcntl.fcntl(self.statusfd.fileno(), fcntl.F_GETFL)
  123.         fcntl.fcntl(self.statusfd.fileno(), fcntl.F_SETFL,
  124.                     flags & ~os.O_NONBLOCK)
  125.  
  126.     def startUpdate(self):
  127.         self.db.progress('START', 0, 100, self.title)
  128.         self.started = True
  129.  
  130.     def error(self, pkg, errormsg):
  131.         if self.error_template is not None:
  132.             self.db.subst(self.error_template, 'PACKAGE', pkg)
  133.             self.db.subst(self.error_template, 'MESSAGE', errormsg)
  134.             self.db.input('critical', self.error_template)
  135.             self.db.go()
  136.  
  137.     def statusChange(self, pkg, percent, status):
  138.         self.percent = percent
  139.         self.status = status
  140.         self.db.progress('SET', int(percent))
  141.         self.db.subst(self.info, 'DESCRIPTION', status)
  142.         self.db.progress('INFO', self.info)
  143.  
  144.     def updateInterface(self):
  145.         # TODO cjwatson 2006-02-28: InstallProgress.updateInterface doesn't
  146.         # give us a handy way to spot when percentages/statuses change and
  147.         # aren't pmerror/pmconffile, so we have to reimplement it here.
  148.         if self.statusfd is None:
  149.             return False
  150.         try:
  151.             while not self.read.endswith("\n"):
  152.                 r = os.read(self.statusfd.fileno(),1)
  153.                 if not r:
  154.                     return False
  155.                 self.read += r
  156.         except OSError, (err,errstr):
  157.             print errstr
  158.         if self.read.endswith("\n"):
  159.             s = self.read
  160.             (status, pkg, percent, status_str) = s.split(":", 3)
  161.             if status == "pmerror":
  162.                 self.error(pkg, status_str)
  163.             elif status == "pmconffile":
  164.                 # we get a string like this:
  165.                 # 'current-conffile' 'new-conffile' useredited distedited
  166.                 match = re.compile("\s*\'(.*)\'\s*\'(.*)\'.*").match(status_str)
  167.                 if match:
  168.                     self.conffile(match.group(1), match.group(2))
  169.             else:
  170.                 self.statusChange(pkg, float(percent), status_str.strip())
  171.             self.read = ""
  172.         return True
  173.  
  174.     def run(self, pm):
  175.         # Create a subprocess to deal with turning apt status messages into
  176.         # debconf protocol messages.
  177.         child_pid = self.fork()
  178.         if child_pid == 0:
  179.             # child
  180.             os.close(self.writefd)
  181.             try:
  182.                 while self.updateInterface():
  183.                     pass
  184.             except (KeyboardInterrupt, SystemExit):
  185.                 pass # we're going to exit anyway
  186.             except:
  187.                 for line in traceback.format_exc().split('\n'):
  188.                     syslog.syslog(syslog.LOG_WARNING, line)
  189.             os._exit(0)
  190.  
  191.         self.statusfd.close()
  192.  
  193.         # Redirect stdin from /dev/null and stdout to stderr to avoid them
  194.         # interfering with our debconf protocol stream.
  195.         saved_stdin = os.dup(0)
  196.         try:
  197.             null = os.open('/dev/null', os.O_RDONLY)
  198.             os.dup2(null, 0)
  199.             os.close(null)
  200.         except OSError:
  201.             pass
  202.         saved_stdout = os.dup(1)
  203.         os.dup2(2, 1)
  204.  
  205.         # Make sure all packages are installed non-interactively. We
  206.         # don't have enough passthrough magic here to deal with any
  207.         # debconf questions they might ask.
  208.         saved_environ_keys = ('DEBIAN_FRONTEND', 'DEBIAN_HAS_FRONTEND',
  209.                               'DEBCONF_USE_CDEBCONF')
  210.         saved_environ = {}
  211.         for key in saved_environ_keys:
  212.             if key in os.environ:
  213.                 saved_environ[key] = os.environ[key]
  214.         os.environ['DEBIAN_FRONTEND'] = 'noninteractive'
  215.         if 'DEBIAN_HAS_FRONTEND' in os.environ:
  216.             del os.environ['DEBIAN_HAS_FRONTEND']
  217.         if 'DEBCONF_USE_CDEBCONF' in os.environ:
  218.             # Probably not a good idea to use this in /target too ...
  219.             del os.environ['DEBCONF_USE_CDEBCONF']
  220.  
  221.         res = pm.ResultFailed
  222.         try:
  223.             res = pm.DoInstall(self.writefd)
  224.         finally:
  225.             # Reap the status-to-debconf subprocess.
  226.             os.close(self.writefd)
  227.             while True:
  228.                 try:
  229.                     (pid, status) = os.waitpid(child_pid, 0)
  230.                     if pid != child_pid:
  231.                         break
  232.                     if os.WIFEXITED(status) or os.WIFSIGNALED(status):
  233.                         break
  234.                 except OSError:
  235.                     break
  236.  
  237.             # Put back stdin and stdout.
  238.             os.dup2(saved_stdin, 0)
  239.             os.close(saved_stdin)
  240.             os.dup2(saved_stdout, 1)
  241.             os.close(saved_stdout)
  242.  
  243.             # Put back the environment.
  244.             for key in saved_environ_keys:
  245.                 if key in saved_environ:
  246.                     os.environ[key] = saved_environ[key]
  247.                 elif key in os.environ:
  248.                     del os.environ[key]
  249.  
  250.         return res
  251.  
  252.     def finishUpdate(self):
  253.         if self.started:
  254.             self.db.progress('STOP')
  255.             self.started = False
  256.  
  257. class InstallStepError(Exception):
  258.     """Raised when an install step fails.
  259.  
  260.     Attributes:
  261.         message -- message returned with exception
  262.  
  263.     """
  264.  
  265.     def __init__(self, message):
  266.         Exception.__init__(self, message)
  267.         self.message = message
  268.  
  269. class Install:
  270.  
  271.     def __init__(self):
  272.         """Initial attributes."""
  273.  
  274.         if os.path.isdir('/rofs'):
  275.             self.source = '/rofs'
  276.         elif os.path.isdir('/UNIONFS'):
  277.             # Klaus Knopper says this may not actually work very well
  278.             # because it'll copy the WHOLE WORLD (~12GB).
  279.             self.source = '/UNIONFS'
  280.         else:
  281.             self.source = '/var/lib/ubiquity/source'
  282.         self.target = '/target'
  283.         self.kernel_version = platform.release()
  284.         self.db = debconf.Debconf()
  285.  
  286.         self.select_language_packs()
  287.         self.blacklist = {}
  288.         if self.db.get('ubiquity/install/generate-blacklist') == 'true':
  289.             self.db.progress('START', 0, 100, 'ubiquity/install/title')
  290.             self.db.progress('INFO', 'ubiquity/install/blacklist')
  291.             self.generate_blacklist()
  292.  
  293.         apt_pkg.InitConfig()
  294.         apt_pkg.Config.Set("Dir", "/target")
  295.         apt_pkg.Config.Set("Dir::State::status", "/target/var/lib/dpkg/status")
  296.         apt_pkg.Config.Set("APT::GPGV::TrustedKeyring",
  297.                            "/target/etc/apt/trusted.gpg")
  298.         apt_pkg.Config.Set("Acquire::gpgv::Options::",
  299.                            "--ignore-time-conflict")
  300.         apt_pkg.Config.Set("DPkg::Options::", "--root=/target")
  301.         # We don't want apt-listchanges or dpkg-preconfigure, so just clear
  302.         # out the list of pre-installation hooks.
  303.         apt_pkg.Config.Clear("DPkg::Pre-Install-Pkgs")
  304.         apt_pkg.InitSystem()
  305.  
  306.     def excepthook(self, exctype, excvalue, exctb):
  307.         """Crash handler. Dump the traceback to a file so that it can be
  308.         read by the caller."""
  309.  
  310.         if (issubclass(exctype, KeyboardInterrupt) or
  311.             issubclass(exctype, SystemExit)):
  312.             return
  313.  
  314.         tbtext = ''.join(traceback.format_exception(exctype, excvalue, exctb))
  315.         syslog.syslog(syslog.LOG_ERR, "Exception during installation:")
  316.         for line in tbtext.split('\n'):
  317.             syslog.syslog(syslog.LOG_ERR, line)
  318.         tbfile = open('/var/lib/ubiquity/install.trace', 'w')
  319.         print >>tbfile, tbtext
  320.         tbfile.close()
  321.  
  322.         sys.exit(1)
  323.  
  324.     def run(self):
  325.         """Run the install stage: copy everything to the target system, then
  326.         configure it as necessary."""
  327.  
  328.         self.db.progress('START', 0, 100, 'ubiquity/install/title')
  329.         self.db.progress('INFO', 'ubiquity/install/mounting_source')
  330.  
  331.         try:
  332.             if self.source == '/var/lib/ubiquity/source':
  333.                 self.mount_source()
  334.  
  335.             self.db.progress('SET', 1)
  336.             self.db.progress('REGION', 1, 75)
  337.             try:
  338.                 self.copy_all()
  339.             except EnvironmentError, e:
  340.                 if e.errno in (errno.ENOENT, errno.EIO, errno.EFAULT,
  341.                                errno.ENOTDIR, errno.EROFS):
  342.                     if e.filename is None:
  343.                         error_template = 'cd_hd_fault'
  344.                     elif e.filename.startswith('/target'):
  345.                         error_template = 'hd_fault'
  346.                     else:
  347.                         error_template = 'cd_fault'
  348.                     error_template = ('ubiquity/install/copying_error/%s' %
  349.                                       error_template)
  350.                     self.db.subst(error_template, 'ERROR', str(e))
  351.                     self.db.input('critical', error_template)
  352.                     self.db.go()
  353.                     # Exit code 3 signals to the frontend that we have
  354.                     # handled this error.
  355.                     sys.exit(3)
  356.                 elif e.errno == errno.ENOSPC:
  357.                     error_template = 'ubiquity/install/copying_error/no_space'
  358.                     self.db.subst(error_template, 'ERROR', str(e))
  359.                     self.db.input('critical', error_template)
  360.                     self.db.go()
  361.                     sys.exit(3)
  362.                 else:
  363.                     raise
  364.  
  365.             self.db.progress('SET', 75)
  366.             self.db.progress('REGION', 75, 76)
  367.             self.db.progress('INFO', 'ubiquity/install/locales')
  368.             self.configure_locales()
  369.  
  370.             self.db.progress('SET', 76)
  371.             self.db.progress('REGION', 76, 77)
  372.             self.db.progress('INFO', 'ubiquity/install/user')
  373.             self.configure_user()
  374.  
  375.             self.db.progress('SET', 77)
  376.             self.db.progress('REGION', 77, 78)
  377.             self.run_target_config_hooks()
  378.  
  379.             self.db.progress('SET', 78)
  380.             self.db.progress('REGION', 78, 79)
  381.             self.db.progress('INFO', 'ubiquity/install/network')
  382.             self.configure_network()
  383.  
  384.             self.db.progress('SET', 79)
  385.             self.db.progress('REGION', 79, 80)
  386.             self.db.progress('INFO', 'ubiquity/install/apt')
  387.             self.configure_apt()
  388.  
  389.             self.db.progress('SET', 80)
  390.             self.db.progress('REGION', 80, 85)
  391.             # Ignore failures from language pack installation.
  392.             try:
  393.                 self.install_language_packs()
  394.             except InstallStepError:
  395.                 pass
  396.             except IOError:
  397.                 pass
  398.             except SystemError:
  399.                 pass
  400.  
  401.             self.db.progress('SET', 85)
  402.             self.db.progress('REGION', 85, 86)
  403.             self.db.progress('INFO', 'ubiquity/install/timezone')
  404.             self.configure_timezone()
  405.  
  406.             self.db.progress('SET', 86)
  407.             self.db.progress('REGION', 86, 87)
  408.             self.db.progress('INFO', 'ubiquity/install/keyboard')
  409.             self.configure_keyboard()
  410.  
  411.             self.db.progress('SET', 87)
  412.             self.db.progress('REGION', 87, 88)
  413.             self.db.progress('INFO', 'ubiquity/install/migrationassistant')
  414.             self.configure_ma()
  415.  
  416.             self.db.progress('SET', 88)
  417.             self.db.progress('REGION', 88, 89)
  418.             self.remove_unusable_kernels()
  419.  
  420.             self.db.progress('SET', 89)
  421.             self.db.progress('REGION', 89, 93)
  422.             self.db.progress('INFO', 'ubiquity/install/hardware')
  423.             self.configure_hardware()
  424.  
  425.             self.db.progress('SET', 93)
  426.             self.db.progress('REGION', 93, 94)
  427.             self.db.progress('INFO', 'ubiquity/install/bootloader')
  428.             self.configure_bootloader()
  429.  
  430.             self.db.progress('SET', 94)
  431.             self.db.progress('REGION', 94, 95)
  432.             self.db.progress('INFO', 'ubiquity/install/installing')
  433.             self.install_extras()
  434.  
  435.             self.db.progress('SET', 95)
  436.             self.db.progress('REGION', 95, 99)
  437.             self.db.progress('INFO', 'ubiquity/install/removing')
  438.             self.remove_extras()
  439.  
  440.             self.remove_broken_cdrom()
  441.  
  442.             self.db.progress('SET', 99)
  443.             self.db.progress('INFO', 'ubiquity/install/log_files')
  444.             self.copy_logs()
  445.  
  446.             self.db.progress('SET', 100)
  447.         finally:
  448.             self.cleanup()
  449.             try:
  450.                 self.db.progress('STOP')
  451.             except (KeyboardInterrupt, SystemExit):
  452.                 raise
  453.             except:
  454.                 pass
  455.  
  456.  
  457.     def copy_file(self, sourcepath, targetpath, md5_check):
  458.         sourcefh = None
  459.         targetfh = None
  460.         try:
  461.             while 1:
  462.                 sourcefh = open(sourcepath, 'rb')
  463.                 targetfh = open(targetpath, 'wb')
  464.                 if md5_check:
  465.                     sourcehash = md5()
  466.                 while 1:
  467.                     buf = sourcefh.read(16 * 1024)
  468.                     if not buf:
  469.                         break
  470.                     targetfh.write(buf)
  471.                     if md5_check:
  472.                         sourcehash.update(buf)
  473.  
  474.                 if not md5_check:
  475.                     break
  476.                 targetfh.close()
  477.                 targetfh = open(targetpath, 'rb')
  478.                 if md5_check:
  479.                     targethash = md5()
  480.                 while 1:
  481.                     buf = targetfh.read(16 * 1024)
  482.                     if not buf:
  483.                         break
  484.                     targethash.update(buf)
  485.                 if targethash.digest() != sourcehash.digest():
  486.                     if targetfh:
  487.                         targetfh.close()
  488.                     if sourcefh:
  489.                         sourcefh.close()
  490.                     error_template = 'ubiquity/install/copying_error/md5'
  491.                     self.db.subst(error_template, 'FILE', targetpath)
  492.                     self.db.input('critical', error_template)
  493.                     self.db.go()
  494.                     response = self.db.get(error_template)
  495.                     if response == 'skip':
  496.                         break
  497.                     elif response == 'abort':
  498.                         syslog.syslog(syslog.LOG_ERR,
  499.                             'MD5 failure on %s' % targetpath)
  500.                         sys.exit(3)
  501.                     elif response == 'retry':
  502.                         pass
  503.                 else:
  504.                     break
  505.         finally:
  506.             if targetfh:
  507.                 targetfh.close()
  508.             if sourcefh:
  509.                 sourcefh.close()
  510.  
  511.     def find_cd_kernel(self):
  512.         """Find the boot kernel on the CD, if possible."""
  513.  
  514.         release_bits = os.uname()[2].split('-')
  515.         if len(release_bits) >= 3:
  516.             subarch = release_bits[2]
  517.         else:
  518.             subarch = None
  519.  
  520.         for prefix in ('vmlinux', 'vmlinuz'):
  521.             kernel = os.path.join('/cdrom/casper', prefix)
  522.             if os.path.exists(kernel):
  523.                 return kernel
  524.  
  525.             if subarch:
  526.                 kernel = os.path.join('/cdrom/casper', subarch, prefix)
  527.                 if os.path.exists(kernel):
  528.                     return kernel
  529.  
  530.                 kernel = os.path.join('/cdrom/casper',
  531.                                       '%s-%s' % (prefix, subarch))
  532.                 if os.path.exists(kernel):
  533.                     return kernel
  534.  
  535.         return None
  536.  
  537.     def generate_blacklist(self):
  538.         if (os.path.exists("/cdrom/casper/filesystem.manifest-desktop") and
  539.             os.path.exists("/cdrom/casper/filesystem.manifest")):
  540.             desktop_packages = set()
  541.             manifest = open("/cdrom/casper/filesystem.manifest-desktop")
  542.             for line in manifest:
  543.                 if line.strip() != '' and not line.startswith('#'):
  544.                     desktop_packages.add(line.split()[0])
  545.             manifest.close()
  546.             live_packages = set()
  547.             manifest = open("/cdrom/casper/filesystem.manifest")
  548.             for line in manifest:
  549.                 if line.strip() != '' and not line.startswith('#'):
  550.                     live_packages.add(line.split()[0])
  551.             manifest.close()
  552.             difference = live_packages - desktop_packages
  553.         else:
  554.             difference = set()
  555.         
  556.         # Keep packages we explicitly installed.
  557.         difference -= self.query_recorded_installed()
  558.         archdetect = subprocess.Popen(['archdetect'], stdout=subprocess.PIPE)
  559.         subarch = archdetect.communicate()[0].strip()
  560.  
  561.         # Less than ideal.  Since we cannot know which bootloader we'll need
  562.         # at file copy time, we should figure out why grub still fails when
  563.         # apt-install-direct is present during configure_bootloader (code
  564.         # removed).
  565.         if subarch.startswith('amd64/') or subarch.startswith('i386/') or subarch.startswith('lpia/'):
  566.             difference -= set(['grub'])
  567.         elif subarch == 'powerpc/ps3':
  568.             pass
  569.         elif subarch.startswith('powerpc/'):
  570.             difference -= set(['yaboot', 'hfsutils'])
  571.  
  572.         if len(difference) == 0:
  573.             self.blacklist = {}
  574.             return
  575.  
  576.         use_restricted = True
  577.         try:
  578.             if self.db.get('apt-setup/restricted') == 'false':
  579.                 use_restricted = False
  580.         except debconf.DebconfError:
  581.             pass
  582.         if not use_restricted:
  583.             cache = Cache()
  584.             for pkg in cache.keys():
  585.                 if (cache[pkg].isInstalled and
  586.                     cache[pkg].section.startswith('restricted/')):
  587.                     difference.add(pkg)
  588.             del cache
  589.         cache = Cache()
  590.         confirmed_remove = set()
  591.         for pkg in sorted(difference):
  592.             if pkg in confirmed_remove:
  593.                 continue
  594.             would_remove = self.get_remove_list(cache, [pkg], recursive=True)
  595.             if would_remove <= difference:
  596.                 confirmed_remove |= would_remove
  597.                 # Leave these marked for removal in the apt cache to speed
  598.                 # up further calculations.
  599.             else:
  600.                 for removedpkg in would_remove:
  601.                     cachedpkg = self.get_cache_pkg(cache, removedpkg)
  602.                     cachedpkg.markKeep()
  603.         difference = confirmed_remove
  604.         difference = set(filter(
  605.             lambda x: not os.path.exists('/var/lib/dpkg/info/%s.prerm' % x),
  606.             difference))
  607.         cmd = ['dpkg', '-L']
  608.         cmd.extend(difference)
  609.         subp = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
  610.         res = subp.communicate()[0].splitlines()
  611.         u = {}
  612.         for x in res:
  613.             u[x] = 1
  614.         self.blacklist = u
  615.  
  616.     def copy_all(self):
  617.         """Core copy process. This is the most important step of this
  618.         stage. It clones live filesystem into a local partition in the
  619.         selected hard disk."""
  620.  
  621.         files = []
  622.         total_size = 0
  623.  
  624.         self.db.progress('START', 0, 100, 'ubiquity/install/title')
  625.         self.db.progress('INFO', 'ubiquity/install/scanning')
  626.  
  627.         # Obviously doing os.walk() twice is inefficient, but I'd rather not
  628.         # suck the list into ubiquity's memory, and I'm guessing that the
  629.         # kernel's dentry cache will avoid most of the slowness anyway.
  630.         walklen = 0
  631.         for entry in os.walk(self.source):
  632.             walklen += 1
  633.         walkpos = 0
  634.         walkprogress = 0
  635.  
  636.         for dirpath, dirnames, filenames in os.walk(self.source):
  637.             walkpos += 1
  638.             if int(float(walkpos) / walklen * 10) != walkprogress:
  639.                 walkprogress = int(float(walkpos) / walklen * 10)
  640.                 self.db.progress('SET', walkprogress)
  641.  
  642.             sourcepath = dirpath[len(self.source) + 1:]
  643.  
  644.             for name in dirnames + filenames:
  645.                 relpath = os.path.join(sourcepath, name)
  646.                 fqpath = os.path.join(dirpath, name)
  647.                 # /etc/fstab was legitimately created by partman, and
  648.                 # shouldn't be copied again.
  649.                 if relpath != "etc/fstab":
  650.                     total_size += os.lstat(fqpath).st_size
  651.                     files.append(relpath)
  652.  
  653.         self.db.progress('SET', 10)
  654.         self.db.progress('INFO', 'ubiquity/install/copying')
  655.  
  656.         # Progress bar handling:
  657.         # We sample progress every half-second (assuming time.time() gives
  658.         # us sufficiently good granularity) and use the average of progress
  659.         # over the last minute or so to decide how much time remains. We
  660.         # don't bother displaying any progress for the first ten seconds in
  661.         # order to allow things to settle down, and we only update the "time
  662.         # remaining" indicator at most every two seconds after that.
  663.  
  664.         copy_progress = 0
  665.         copied_size, counter = 0, 0
  666.         directory_times = []
  667.         time_start = time.time()
  668.         times = [(time_start, copied_size)]
  669.         long_enough = False
  670.         time_last_update = time_start
  671.         if self.db.get('ubiquity/install/md5_check') == 'false':
  672.             md5_check = False
  673.         else:
  674.             md5_check = True
  675.         
  676.         old_umask = os.umask(0)
  677.         for path in files:
  678.             sourcepath = os.path.join(self.source, path)
  679.             targetpath = os.path.join(self.target, path)
  680.             st = os.lstat(sourcepath)
  681.             mode = stat.S_IMODE(st.st_mode)
  682.             if stat.S_ISLNK(st.st_mode):
  683.                 if os.path.lexists(targetpath):
  684.                     os.unlink(targetpath)
  685.                 linkto = os.readlink(sourcepath)
  686.                 os.symlink(linkto, targetpath)
  687.             elif stat.S_ISDIR(st.st_mode):
  688.                 if not os.path.isdir(targetpath):
  689.                     os.mkdir(targetpath, mode)
  690.             elif stat.S_ISCHR(st.st_mode):
  691.                 os.mknod(targetpath, stat.S_IFCHR | mode, st.st_rdev)
  692.             elif stat.S_ISBLK(st.st_mode):
  693.                 os.mknod(targetpath, stat.S_IFBLK | mode, st.st_rdev)
  694.             elif stat.S_ISFIFO(st.st_mode):
  695.                 os.mknod(targetpath, stat.S_IFIFO | mode)
  696.             elif stat.S_ISSOCK(st.st_mode):
  697.                 os.mknod(targetpath, stat.S_IFSOCK | mode)
  698.             elif stat.S_ISREG(st.st_mode):
  699.                 if '/%s' % path in self.blacklist:
  700.                     syslog.syslog('Not copying %s' % path)
  701.                     continue
  702.                 if os.path.exists(targetpath):
  703.                     os.unlink(targetpath)
  704.                 self.copy_file(sourcepath, targetpath, md5_check)
  705.  
  706.             copied_size += st.st_size
  707.             os.lchown(targetpath, st.st_uid, st.st_gid)
  708.             if not stat.S_ISLNK(st.st_mode):
  709.                 os.chmod(targetpath, mode)
  710.             if stat.S_ISDIR(st.st_mode):
  711.                 directory_times.append((targetpath, st.st_atime, st.st_mtime))
  712.             # os.utime() sets timestamp of target, not link
  713.             elif not stat.S_ISLNK(st.st_mode):
  714.                 os.utime(targetpath, (st.st_atime, st.st_mtime))
  715.  
  716.             if int((copied_size * 90) / total_size) != copy_progress:
  717.                 copy_progress = int((copied_size * 90) / total_size)
  718.                 self.db.progress('SET', 10 + copy_progress)
  719.  
  720.             time_now = time.time()
  721.             if (time_now - times[-1][0]) >= 0.5:
  722.                 times.append((time_now, copied_size))
  723.                 if not long_enough and time_now - times[0][0] >= 10:
  724.                     long_enough = True
  725.                 if long_enough and time_now - time_last_update >= 2:
  726.                     time_last_update = time_now
  727.                     while (time_now - times[0][0] > 60 and
  728.                            time_now - times[1][0] >= 60):
  729.                         times.pop(0)
  730.                     speed = ((times[-1][1] - times[0][1]) /
  731.                              (times[-1][0] - times[0][0]))
  732.                     if speed != 0:
  733.                         time_remaining = int((total_size - copied_size) / speed)
  734.                         if time_remaining < 60:
  735.                             self.db.progress(
  736.                                 'INFO', 'ubiquity/install/copying_minute')
  737.  
  738.         # Apply timestamps to all directories now that the items within them
  739.         # have been copied.
  740.         for dirtime in directory_times:
  741.             (directory, atime, mtime) = dirtime
  742.             try:
  743.                 os.utime(directory, (atime, mtime))
  744.             except OSError:
  745.                 # I have no idea why I've been getting lots of bug reports
  746.                 # about this failing, but I really don't care. Ignore it.
  747.                 pass
  748.  
  749.         # Try some possible locations for the kernel we used to boot. This
  750.         # lets us save a couple of megabytes of CD space.
  751.         bootdir = os.path.join(self.target, 'boot')
  752.         kernel = self.find_cd_kernel()
  753.         if kernel:
  754.             prefix = os.path.basename(kernel).split('-', 1)[0]
  755.             release = os.uname()[2]
  756.             target_kernel = os.path.join(bootdir, '%s-%s' % (prefix, release))
  757.             if os.path.exists(target_kernel):
  758.                 os.unlink(target_kernel)
  759.             self.copy_file(kernel, target_kernel, md5_check)
  760.             os.lchown(target_kernel, 0, 0)
  761.             os.chmod(target_kernel, 0644)
  762.             st = os.lstat(kernel)
  763.             os.utime(target_kernel, (st.st_atime, st.st_mtime))
  764.  
  765.         os.umask(old_umask)
  766.  
  767.         self.db.progress('SET', 100)
  768.         self.db.progress('STOP')
  769.  
  770.  
  771.     def copy_logs(self):
  772.         """copy log files into installed system."""
  773.  
  774.         target_dir = os.path.join(self.target, 'var/log/installer')
  775.         if not os.path.exists(target_dir):
  776.             os.makedirs(target_dir)
  777.  
  778.         for log_file in ('/var/log/syslog', '/var/log/partman',
  779.                          '/var/log/installer/version', '/var/log/casper.log'):
  780.             target_log_file = os.path.join(target_dir,
  781.                                            os.path.basename(log_file))
  782.             if os.path.isfile(log_file):
  783.                 if not misc.execute('cp', '-a', log_file, target_log_file):
  784.                     syslog.syslog(syslog.LOG_ERR,
  785.                                   'Failed to copy installation log file')
  786.                 os.chmod(target_log_file, stat.S_IRUSR | stat.S_IWUSR)
  787.         try:
  788.             status = open(os.path.join(self.target, 'var/lib/dpkg/status'))
  789.             status_gz = gzip.open(os.path.join(target_dir,
  790.                                                'initial-status.gz'), 'w')
  791.             while True:
  792.                 data = status.read(65536)
  793.                 if not data:
  794.                     break
  795.                 status_gz.write(data)
  796.             status_gz.close()
  797.             status.close()
  798.         except IOError:
  799.             pass
  800.         try:
  801.             if self.db.get('oem-config/enable') == 'true':
  802.                 oem_id = self.db.get('oem-config/id')
  803.                 oem_id_file = open(
  804.                     os.path.join(self.target, 'var/log/installer/oem-id'), 'w')
  805.                 print >>oem_id_file, oem_id
  806.                 oem_id_file.close()
  807.         except (debconf.DebconfError, IOError):
  808.             pass
  809.  
  810.  
  811.     def mount_one_image(self, fsfile, mountpoint=None):
  812.         if os.path.splitext(fsfile)[1] == '.cloop':
  813.             blockdev_prefix = 'cloop'
  814.         elif os.path.splitext(fsfile)[1] == '.squashfs':
  815.             blockdev_prefix = 'loop'
  816.  
  817.         if blockdev_prefix == '':
  818.             raise InstallStepError("No source device found for %s" % fsfile)
  819.  
  820.         dev = ''
  821.         sysloops = filter(lambda x: x.startswith(blockdev_prefix),
  822.                           os.listdir('/sys/block'))
  823.         sysloops.sort()
  824.         for sysloop in sysloops:
  825.             try:
  826.                 sysloopf = open(os.path.join('/sys/block', sysloop, 'size'))
  827.                 sysloopsize = sysloopf.readline().strip()
  828.                 sysloopf.close()
  829.                 if sysloopsize == '0':
  830.                     devnull = open('/dev/null')
  831.                     if osextras.find_on_path('udevadm'):
  832.                         udevinfo_cmd = ['udevadm', 'info']
  833.                     else:
  834.                         udevinfo_cmd = ['udevinfo']
  835.                     udevinfo_cmd.extend(
  836.                         ['-q', 'name', '-p', os.path.join('/block', sysloop)])
  837.                     udevinfo = subprocess.Popen(
  838.                         udevinfo_cmd, stdout=subprocess.PIPE, stderr=devnull)
  839.                     devbase = udevinfo.communicate()[0]
  840.                     devnull.close()
  841.                     if udevinfo.returncode != 0:
  842.                         devbase = sysloop
  843.                     dev = '/dev/%s' % devbase
  844.                     break
  845.             except:
  846.                 continue
  847.  
  848.         if dev == '':
  849.             raise InstallStepError("No loop device available for %s" % fsfile)
  850.  
  851.         misc.execute('losetup', dev, fsfile)
  852.         if mountpoint is None:
  853.             mountpoint = '/var/lib/ubiquity/%s' % sysloop
  854.         if not os.path.isdir(mountpoint):
  855.             os.mkdir(mountpoint)
  856.         if not misc.execute('mount', dev, mountpoint):
  857.             misc.execute('losetup', '-d', dev)
  858.             misc.execute('mount', '-o', 'loop', fsfile, mountpoint)
  859.             dev = 'unused'
  860.  
  861.         return (dev, mountpoint)
  862.  
  863.     def mount_source(self):
  864.         """mounting loop system from cloop or squashfs system."""
  865.  
  866.         self.devs = []
  867.         self.mountpoints = []
  868.  
  869.         if not os.path.isdir(self.source):
  870.             syslog.syslog('mkdir %s' % self.source)
  871.             os.mkdir(self.source)
  872.  
  873.         fs_preseed = self.db.get('ubiquity/install/filesystem-images')
  874.  
  875.         if fs_preseed == '':
  876.             # Simple autodetection on unionfs systems
  877.             mounts = open('/proc/mounts')
  878.             for line in mounts:
  879.                 (device, fstype) = line.split()[1:3]
  880.                 if fstype == 'squashfs' and os.path.exists(device):
  881.                     misc.execute('mount', '--bind', device, self.source)
  882.                     self.mountpoints.append(self.source)
  883.                     mounts.close()
  884.                     return
  885.             mounts.close()
  886.  
  887.             # Manual detection on non-unionfs systems
  888.             fsfiles = ['/cdrom/casper/filesystem.cloop',
  889.                        '/cdrom/casper/filesystem.squashfs',
  890.                        '/cdrom/META/META.squashfs',
  891.                        '/live/image/live/filesystem.squashfs']
  892.  
  893.             for fsfile in fsfiles:
  894.                 if fsfile != '' and os.path.isfile(fsfile):
  895.                     dev, mountpoint = self.mount_one_image(fsfile, self.source)
  896.                     self.devs.append(dev)
  897.                     self.mountpoints.append(mountpoint)
  898.  
  899.         elif len(fs_preseed.split()) == 1:
  900.             # Just one preseeded image.
  901.             if not os.path.isfile(fs_preseed):
  902.                 raise InstallStepError(
  903.                     "Preseeded filesystem image %s not found" % fs_preseed)
  904.  
  905.                 dev, mountpoint = self.mount_one_image(fsfile, self.source)
  906.                 self.devs.append(dev)
  907.                 self.mountpoints.append(mountpoint)
  908.         else:
  909.             # OK, so we need to mount multiple images and unionfs them
  910.             # together.
  911.             for fsfile in fs_preseed.split():
  912.                 if not os.path.isfile(fsfile):
  913.                     raise InstallStepError(
  914.                         "Preseeded filesystem image %s not found" % fsfile)
  915.  
  916.                 dev, mountpoint = self.mount_one_image(fsfile)
  917.                 self.devs.append(dev)
  918.                 self.mountpoints.append(mountpoint)
  919.  
  920.             assert self.devs
  921.             assert self.mountpoints
  922.  
  923.             misc.execute('mount', '-t', 'unionfs', '-o',
  924.                          'dirs=' + ':'.join(map(lambda x: '%s=ro' % x,
  925.                                                 self.mountpoints)),
  926.                          'unionfs', self.source)
  927.             self.mountpoints.append(self.source)
  928.  
  929.     def umount_source(self):
  930.         """umounting loop system from cloop or squashfs system."""
  931.  
  932.         devs = self.devs
  933.         devs.reverse()
  934.         mountpoints = self.mountpoints
  935.         mountpoints.reverse()
  936.  
  937.         for mountpoint in mountpoints:
  938.             if not misc.execute('umount', mountpoint):
  939.                 raise InstallStepError("Failed to unmount %s" % mountpoint)
  940.         for dev in devs:
  941.             if (dev != '' and dev != 'unused' and
  942.                 not misc.execute('losetup', '-d', dev)):
  943.                 raise InstallStepError(
  944.                     "Failed to detach loopback device %s" % dev)
  945.  
  946.  
  947.     def chroot_setup(self, x11=False):
  948.         """Set up /target for safe package management operations."""
  949.         policy_rc_d = os.path.join(self.target, 'usr/sbin/policy-rc.d')
  950.         f = open(policy_rc_d, 'w')
  951.         print >>f, """\
  952. #!/bin/sh
  953. exit 101"""
  954.         f.close()
  955.         os.chmod(policy_rc_d, 0755)
  956.  
  957.         start_stop_daemon = os.path.join(self.target, 'sbin/start-stop-daemon')
  958.         if os.path.exists(start_stop_daemon):
  959.             os.rename(start_stop_daemon, '%s.REAL' % start_stop_daemon)
  960.         f = open(start_stop_daemon, 'w')
  961.         print >>f, """\
  962. #!/bin/sh
  963. echo 1>&2
  964. echo 'Warning: Fake start-stop-daemon called, doing nothing.' 1>&2
  965. exit 0"""
  966.         f.close()
  967.         os.chmod(start_stop_daemon, 0755)
  968.  
  969.         if not os.path.exists(os.path.join(self.target, 'proc/cmdline')):
  970.             self.chrex('mount', '-t', 'proc', 'proc', '/proc')
  971.         if not os.path.exists(os.path.join(self.target, 'sys/devices')):
  972.             self.chrex('mount', '-t', 'sysfs', 'sysfs', '/sys')
  973.  
  974.         if x11 and 'DISPLAY' in os.environ:
  975.             if 'SUDO_USER' in os.environ:
  976.                 xauthority = os.path.expanduser('~%s/.Xauthority' %
  977.                                                 os.environ['SUDO_USER'])
  978.             else:
  979.                 xauthority = os.path.expanduser('~/.Xauthority')
  980.             if os.path.exists(xauthority):
  981.                 shutil.copy(xauthority,
  982.                             os.path.join(self.target, 'root/.Xauthority'))
  983.  
  984.             if not os.path.isdir(os.path.join(self.target, 'tmp/.X11-unix')):
  985.                 os.mkdir(os.path.join(self.target, 'tmp/.X11-unix'))
  986.             misc.execute('mount', '--bind', '/tmp/.X11-unix',
  987.                          os.path.join(self.target, 'tmp/.X11-unix'))
  988.  
  989.     def chroot_cleanup(self, x11=False):
  990.         """Undo the work done by chroot_setup."""
  991.         if x11 and 'DISPLAY' in os.environ:
  992.             misc.execute('umount', os.path.join(self.target, 'tmp/.X11-unix'))
  993.             try:
  994.                 os.rmdir(os.path.join(self.target, 'tmp/.X11-unix'))
  995.             except OSError:
  996.                 pass
  997.             try:
  998.                 os.unlink(os.path.join(self.target, 'root/.Xauthority'))
  999.             except OSError:
  1000.                 pass
  1001.  
  1002.         self.chrex('umount', '/sys')
  1003.         self.chrex('umount', '/proc')
  1004.  
  1005.         start_stop_daemon = os.path.join(self.target, 'sbin/start-stop-daemon')
  1006.         os.rename('%s.REAL' % start_stop_daemon, start_stop_daemon)
  1007.  
  1008.         policy_rc_d = os.path.join(self.target, 'usr/sbin/policy-rc.d')
  1009.         os.unlink(policy_rc_d)
  1010.  
  1011.  
  1012.     def run_target_config_hooks(self):
  1013.         """Run hook scripts from /usr/lib/ubiquity/target-config. This allows
  1014.         casper to hook into us and repeat bits of its configuration in the
  1015.         target system."""
  1016.  
  1017.         hookdir = '/usr/lib/ubiquity/target-config'
  1018.  
  1019.         if os.path.isdir(hookdir):
  1020.             # Exclude hooks containing '.', so that *.dpkg-* et al are avoided.
  1021.             hooks = filter(lambda entry: '.' not in entry, os.listdir(hookdir))
  1022.             self.db.progress('START', 0, len(hooks), 'ubiquity/install/title')
  1023.             self.db.progress('INFO', 'ubiquity/install/target_hooks')
  1024.             for hookentry in hooks:
  1025.                 hook = os.path.join(hookdir, hookentry)
  1026.                 if not os.access(hook, os.X_OK):
  1027.                     self.db.progress('STEP', 1)
  1028.                     continue
  1029.                 # Errors are ignored at present, although this may change.
  1030.                 subprocess.call(['log-output', '-t', 'ubiquity',
  1031.                                  '--pass-stdout', hook])
  1032.                 self.db.progress('STEP', 1)
  1033.             self.db.progress('STOP')
  1034.  
  1035.  
  1036.     def configure_locales(self):
  1037.         """Apply locale settings to installed system."""
  1038.         dbfilter = language_apply.LanguageApply(None)
  1039.         ret = dbfilter.run_command(auto_process=True)
  1040.         if ret != 0:
  1041.             raise InstallStepError("LanguageApply failed with code %d" % ret)
  1042.  
  1043.         # fontconfig configuration needs to be adjusted based on the
  1044.         # selected locale (from language-selector-common.postinst). Ignore
  1045.         # errors.
  1046.         self.chrex('fontconfig-voodoo', '--auto', '--force', '--quiet')
  1047.  
  1048.  
  1049.     def configure_apt(self):
  1050.         """Configure /etc/apt/sources.list."""
  1051.  
  1052.         # TODO cjwatson 2007-07-06: Much of the following is
  1053.         # cloned-and-hacked from base-installer/debian/postinst. Perhaps we
  1054.         # should come up with a way to avoid this.
  1055.  
  1056.         # Make apt trust CDs. This is not on by default (we think).
  1057.         # This will be left in place on the installed system.
  1058.         apt_conf_tc = open(os.path.join(
  1059.             self.target, 'etc/apt/apt.conf.d/00trustcdrom'), 'w')
  1060.         print >>apt_conf_tc, 'APT::Authentication::TrustCDROM "true";'
  1061.         apt_conf_tc.close()
  1062.  
  1063.         # Avoid clock skew causing gpg verification issues.
  1064.         # This file will be left in place until the end of the install.
  1065.         apt_conf_itc = open(os.path.join(
  1066.             self.target, 'etc/apt/apt.conf.d/00IgnoreTimeConflict'), 'w')
  1067.         print >>apt_conf_itc, \
  1068.             'Acquire::gpgv::Options { "--ignore-time-conflict"; };'
  1069.         apt_conf_itc.close()
  1070.  
  1071.         try:
  1072.             if self.db.get('debian-installer/allow_unauthenticated') == 'true':
  1073.                 apt_conf_au = open(
  1074.                     os.path.join(self.target,
  1075.                                  'etc/apt/apt.conf.d/00AllowUnauthenticated'),
  1076.                     'w')
  1077.                 print >>apt_conf_au, 'APT::Get::AllowUnauthenticated "true";'
  1078.                 print >>apt_conf_au, \
  1079.                     'Aptitude::CmdLine::Ignore-Trust-Violations "true";'
  1080.                 apt_conf_au.close()
  1081.         except debconf.DebconfError:
  1082.             pass
  1083.  
  1084.         # let apt inside the chroot see the cdrom
  1085.         target_cdrom = os.path.join(self.target, 'cdrom')
  1086.         misc.execute('umount', target_cdrom)
  1087.         if not os.path.exists(target_cdrom):
  1088.             os.mkdir(target_cdrom)
  1089.         misc.execute('mount', '--bind', '/cdrom', target_cdrom)
  1090.  
  1091.         # Make apt-cdrom and apt not unmount/mount CD-ROMs.
  1092.         # This file will be left in place until the end of the install.
  1093.         apt_conf_nmc = open(os.path.join(
  1094.             self.target, 'etc/apt/apt.conf.d/00NoMountCDROM'), 'w')
  1095.         print >>apt_conf_nmc, textwrap.dedent("""\
  1096.             APT::CDROM::NoMount "true";
  1097.             Acquire::cdrom {
  1098.               mount "/cdrom";
  1099.               "/cdrom/" {
  1100.                 Mount  "true";
  1101.                 UMount "true";
  1102.               };
  1103.             }""")
  1104.         apt_conf_nmc.close()
  1105.  
  1106.         dbfilter = apt_setup.AptSetup(None, self.db)
  1107.         ret = dbfilter.run_command(auto_process=True)
  1108.         if ret != 0:
  1109.             raise InstallStepError("AptSetup failed with code %d" % ret)
  1110.  
  1111.  
  1112.     def get_cache_pkg(self, cache, pkg):
  1113.         # work around broken has_key in python-apt 0.6.16
  1114.         try:
  1115.             return cache[pkg]
  1116.         except KeyError:
  1117.             return None
  1118.  
  1119.  
  1120.     def record_installed(self, pkgs):
  1121.         """Record which packages we've explicitly installed so that we don't
  1122.         try to remove them later."""
  1123.  
  1124.         record_file = "/var/lib/ubiquity/apt-installed"
  1125.         if not os.path.exists(os.path.dirname(record_file)):
  1126.             os.makedirs(os.path.dirname(record_file))
  1127.         record = open(record_file, "a")
  1128.  
  1129.         for pkg in pkgs:
  1130.             print >>record, pkg
  1131.  
  1132.         record.close()
  1133.  
  1134.  
  1135.     def query_recorded_installed(self):
  1136.         apt_installed = set()
  1137.         if os.path.exists("/var/lib/ubiquity/apt-installed"):
  1138.             record_file = open("/var/lib/ubiquity/apt-installed")
  1139.             for line in record_file:
  1140.                 apt_installed.add(line.strip())
  1141.             record_file.close()
  1142.         return apt_installed
  1143.  
  1144.  
  1145.     def mark_install(self, cache, pkg):
  1146.         cachedpkg = self.get_cache_pkg(cache, pkg)
  1147.         if cachedpkg is not None and not cachedpkg.isInstalled:
  1148.             apt_error = False
  1149.             try:
  1150.                 cachedpkg.markInstall()
  1151.             except SystemError:
  1152.                 apt_error = True
  1153.             if cache._depcache.BrokenCount > 0 or apt_error:
  1154.                 brokenpkgs = self.broken_packages(cache)
  1155.                 while brokenpkgs:
  1156.                     for brokenpkg in brokenpkgs:
  1157.                         self.get_cache_pkg(cache, brokenpkg).markKeep()
  1158.                     new_brokenpkgs = self.broken_packages(cache)
  1159.                     if brokenpkgs == new_brokenpkgs:
  1160.                         break # we can do nothing more
  1161.                     brokenpkgs = new_brokenpkgs
  1162.                 assert cache._depcache.BrokenCount == 0
  1163.  
  1164.  
  1165.     def select_language_packs(self):
  1166.         langpacks = []
  1167.         try:
  1168.             langpack_db = self.db.get('pkgsel/language-packs')
  1169.             langpacks = langpack_db.replace(',', '').split()
  1170.         except debconf.DebconfError:
  1171.             pass
  1172.         if not langpacks:
  1173.             try:
  1174.                 langpack_db = self.db.get('localechooser/supported-locales')
  1175.                 langpack_set = set()
  1176.                 for locale in langpack_db.replace(',', '').split():
  1177.                     langpack_set.add(locale.split('_')[0])
  1178.                 langpacks = sorted(langpack_set)
  1179.             except debconf.DebconfError:
  1180.                 pass
  1181.         if not langpacks:
  1182.             langpack_db = self.db.get('debian-installer/locale')
  1183.             langpacks = [langpack_db.split('_')[0]]
  1184.         syslog.syslog('keeping language packs for: %s' % ' '.join(langpacks))
  1185.  
  1186.         try:
  1187.             lppatterns = self.db.get('pkgsel/language-pack-patterns').split()
  1188.         except debconf.DebconfError:
  1189.             return
  1190.  
  1191.         to_install = []
  1192.         for lp in langpacks:
  1193.             # Basic language packs, required to get localisation working at
  1194.             # all. We install these almost unconditionally; if you want to
  1195.             # get rid of even these, you can preseed pkgsel/language-packs
  1196.             # to the empty string.
  1197.             to_install.append('language-pack-%s' % lp)
  1198.             # Other language packs, typically selected by preseeding.
  1199.             for pattern in lppatterns:
  1200.                 to_install.append(pattern.replace('$LL', lp))
  1201.             # More extensive language support packages.
  1202.             to_install.append('language-support-%s' % lp)
  1203.  
  1204.         self.record_installed(to_install)
  1205.         self.langpacks = to_install
  1206.  
  1207.     def install_language_packs(self):
  1208.         self.do_install(self.langpacks)
  1209.  
  1210.         cache = Cache()
  1211.         incomplete = False
  1212.         for pkg in self.langpacks:
  1213.             cachedpkg = self.get_cache_pkg(cache, pkg)
  1214.             if cachedpkg is None or not cachedpkg.isInstalled:
  1215.                 incomplete = True
  1216.                 break
  1217.         if incomplete:
  1218.             language_support_dir = \
  1219.                 os.path.join(self.target, 'usr/share/language-support')
  1220.             update_notifier_dir = \
  1221.                 os.path.join(self.target, 'var/lib/update-notifier/user.d')
  1222.             for note in ('incomplete-language-support-gnome.note',
  1223.                          'incomplete-language-support-qt.note'):
  1224.                 notepath = os.path.join(language_support_dir, note)
  1225.                 if os.path.exists(notepath):
  1226.                     if not os.path.exists(update_notifier_dir):
  1227.                         os.makedirs(update_notifier_dir)
  1228.                     shutil.copy(notepath,
  1229.                                 os.path.join(update_notifier_dir, note))
  1230.                     break
  1231.  
  1232.  
  1233.     def configure_timezone(self):
  1234.         """Set timezone on installed system."""
  1235.  
  1236.         dbfilter = timezone_apply.TimezoneApply(None)
  1237.         ret = dbfilter.run_command(auto_process=True)
  1238.         if ret != 0:
  1239.             raise InstallStepError("TimezoneApply failed with code %d" % ret)
  1240.  
  1241.         dbfilter = clock_setup.ClockSetup(None)
  1242.         ret = dbfilter.run_command(auto_process=True)
  1243.         if ret != 0:
  1244.             raise InstallStepError("ClockSetup failed with code %d" % ret)
  1245.  
  1246.  
  1247.     def configure_keyboard(self):
  1248.         """Set keyboard in installed system."""
  1249.  
  1250.         dbfilter = console_setup_apply.ConsoleSetupApply(None)
  1251.         ret = dbfilter.run_command(auto_process=True)
  1252.         if ret != 0:
  1253.             raise InstallStepError(
  1254.                 "ConsoleSetupApply failed with code %d" % ret)
  1255.  
  1256.  
  1257.     def configure_user(self):
  1258.         """create the user selected along the installation process
  1259.         into the installed system. Default user from live system is
  1260.         deleted and skel for this new user is copied to $HOME."""
  1261.  
  1262.         dbfilter = usersetup_apply.UserSetupApply(None)
  1263.         ret = dbfilter.run_command(auto_process=True)
  1264.         if ret != 0:
  1265.             raise InstallStepError("UserSetupApply failed with code %d" % ret)
  1266.  
  1267.     def configure_ma(self):
  1268.         """import documents, settings, and users from previous operating
  1269.         systems."""
  1270.  
  1271.         if 'UBIQUITY_MIGRATION_ASSISTANT' in os.environ:
  1272.             dbfilter = migrationassistant_apply.MigrationAssistantApply(None)
  1273.             ret = dbfilter.run_command(auto_process=True)
  1274.             if ret != 0:
  1275.                 raise InstallStepError("MigrationAssistantApply failed with code %d" % ret)
  1276.  
  1277.  
  1278.     def get_resume_partition(self):
  1279.         biggest_size = 0
  1280.         biggest_partition = None
  1281.         swaps = open('/proc/swaps')
  1282.         for line in swaps:
  1283.             words = line.split()
  1284.             if words[1] != 'partition':
  1285.                 continue
  1286.             if words[0].startswith('/dev/ramzswap'):
  1287.                 continue
  1288.             size = int(words[2])
  1289.             if size > biggest_size:
  1290.                 biggest_size = size
  1291.                 biggest_partition = words[0]
  1292.         swaps.close()
  1293.         return biggest_partition
  1294.  
  1295.     def configure_hardware(self):
  1296.         """reconfiguring several packages which depends on the
  1297.         hardware system in which has been installed on and need some
  1298.         automatic configurations to get work."""
  1299.  
  1300.         self.chroot_setup()
  1301.         try:
  1302.             dbfilter = hw_detect.HwDetect(None, self.db)
  1303.             ret = dbfilter.run_command(auto_process=True)
  1304.             if ret != 0:
  1305.                 raise InstallStepError("HwDetect failed with code %d" % ret)
  1306.         finally:
  1307.             self.chroot_cleanup()
  1308.  
  1309.         self.db.progress('INFO', 'ubiquity/install/hardware')
  1310.  
  1311.         misc.execute('/usr/lib/ubiquity/debian-installer-utils'
  1312.                      '/register-module.post-base-installer')
  1313.  
  1314.         resume = self.get_resume_partition()
  1315.         if resume is not None:
  1316.             resume_uuid = None
  1317.             try:
  1318.                 resume_uuid = subprocess.Popen(
  1319.                     ['vol_id', '-u', resume],
  1320.                     stdout=subprocess.PIPE).communicate()[0].rstrip('\n')
  1321.             except OSError:
  1322.                 pass
  1323.             if resume_uuid:
  1324.                 resume = "UUID=%s" % resume_uuid
  1325.             if os.path.exists(os.path.join(self.target,
  1326.                                            'etc/initramfs-tools/conf.d')):
  1327.                 configdir = os.path.join(self.target,
  1328.                                          'etc/initramfs-tools/conf.d')
  1329.             elif os.path.exists(os.path.join(self.target,
  1330.                                              'etc/mkinitramfs/conf.d')):
  1331.                 configdir = os.path.join(self.target,
  1332.                                          'etc/mkinitramfs/conf.d')
  1333.             else:
  1334.                 configdir = None
  1335.             if configdir is not None:
  1336.                 configfile = open(os.path.join(configdir, 'resume'), 'w')
  1337.                 print >>configfile, "RESUME=%s" % resume
  1338.                 configfile.close()
  1339.  
  1340.         try:
  1341.             os.unlink('/target/etc/usplash.conf')
  1342.         except OSError:
  1343.             pass
  1344.         try:
  1345.             modes = self.db.get('xserver-xorg/config/display/modes')
  1346.             self.set_debconf('xserver-xorg/config/display/modes', modes)
  1347.         except debconf.DebconfError:
  1348.             pass
  1349.  
  1350.         try:
  1351.             os.unlink('/target/etc/popularity-contest.conf')
  1352.         except OSError:
  1353.             pass
  1354.         try:
  1355.             participate = self.db.get('popularity-contest/participate')
  1356.             self.set_debconf('popularity-contest/participate', participate)
  1357.         except debconf.DebconfError:
  1358.             pass
  1359.  
  1360.         try:
  1361.             os.unlink('/target/etc/papersize')
  1362.         except OSError:
  1363.             pass
  1364.         subprocess.call(['log-output', '-t', 'ubiquity', 'chroot', self.target,
  1365.                          'ucf', '--purge', '/etc/papersize'],
  1366.                         preexec_fn=debconf_disconnect, close_fds=True)
  1367.         try:
  1368.             self.set_debconf('libpaper/defaultpaper', '')
  1369.         except debconf.DebconfError:
  1370.             pass
  1371.  
  1372.         try:
  1373.             os.unlink('/target/etc/ssl/certs/ssl-cert-snakeoil.pem')
  1374.         except OSError:
  1375.             pass
  1376.         try:
  1377.             os.unlink('/target/etc/ssl/private/ssl-cert-snakeoil.key')
  1378.         except OSError:
  1379.             pass
  1380.  
  1381.         self.chroot_setup(x11=True)
  1382.         self.chrex('dpkg-divert', '--package', 'ubiquity', '--rename',
  1383.                    '--quiet', '--add', '/usr/sbin/update-initramfs')
  1384.         try:
  1385.             os.symlink('/bin/true', '/target/usr/sbin/update-initramfs')
  1386.         except OSError:
  1387.             pass
  1388.  
  1389.         packages = ['linux-image-' + self.kernel_version,
  1390.                     'linux-restricted-modules-' + self.kernel_version,
  1391.                     'usplash',
  1392.                     'splashy',
  1393.                     'popularity-contest',
  1394.                     'libpaper1',
  1395.                     'ssl-cert']
  1396.  
  1397.         try:
  1398.             for package in packages:
  1399.                 self.reconfigure(package)
  1400.         finally:
  1401.             try:
  1402.                 os.unlink('/target/usr/sbin/update-initramfs')
  1403.             except OSError:
  1404.                 pass
  1405.             self.chrex('dpkg-divert', '--package', 'ubiquity', '--rename',
  1406.                        '--quiet', '--remove', '/usr/sbin/update-initramfs')
  1407.             self.chrex('update-initramfs', '-c', '-k', os.uname()[2])
  1408.             self.chroot_cleanup(x11=True)
  1409.  
  1410.         # Fix up kernel symlinks now that the initrd exists. Depending on
  1411.         # the architecture, these may be in / or in /boot.
  1412.         bootdir = os.path.join(self.target, 'boot')
  1413.         if self.db.get('base-installer/kernel/linux/link_in_boot') == 'true':
  1414.             linkdir = bootdir
  1415.             linkprefix = ''
  1416.         else:
  1417.             linkdir = self.target
  1418.             linkprefix = 'boot'
  1419.  
  1420.         # Remove old symlinks. We'll set them up from scratch.
  1421.         re_symlink = re.compile('vmlinu[xz]|initrd.img$')
  1422.         for entry in os.listdir(linkdir):
  1423.             if re_symlink.match(entry) is not None:
  1424.                 filename = os.path.join(linkdir, entry)
  1425.                 if os.path.islink(filename):
  1426.                     os.unlink(filename)
  1427.         if linkdir != self.target:
  1428.             # Remove symlinks in /target too, which may have been created on
  1429.             # the live filesystem. This isn't necessary, but it may help
  1430.             # avoid confusion.
  1431.             for entry in os.listdir(self.target):
  1432.                 if re_symlink.match(entry) is not None:
  1433.                     filename = os.path.join(self.target, entry)
  1434.                     if os.path.islink(filename):
  1435.                         os.unlink(filename)
  1436.  
  1437.         # Create symlinks. Prefer our current kernel version if possible,
  1438.         # but if not (perhaps due to a customised live filesystem image),
  1439.         # it's better to create some symlinks than none at all.
  1440.         re_image = re.compile('(vmlinu[xz]|initrd.img)-')
  1441.         for entry in os.listdir(bootdir):
  1442.             match = re_image.match(entry)
  1443.             if match is not None:
  1444.                 imagetype = match.group(1)
  1445.                 linksrc = os.path.join(linkprefix, entry)
  1446.                 linkdst = os.path.join(linkdir, imagetype)
  1447.                 if os.path.exists(linkdst):
  1448.                     if entry.endswith('-' + self.kernel_version):
  1449.                         os.unlink(linkdst)
  1450.                     else:
  1451.                         continue
  1452.                 os.symlink(linksrc, linkdst)
  1453.  
  1454.  
  1455.     def get_all_interfaces(self):
  1456.         """Get all non-local network interfaces."""
  1457.         ifs = []
  1458.         ifs_file = open('/proc/net/dev')
  1459.         # eat header
  1460.         ifs_file.readline()
  1461.         ifs_file.readline()
  1462.  
  1463.         for line in ifs_file:
  1464.             name = re.match('(.*?(?::\d+)?):', line.strip()).group(1)
  1465.             if name == 'lo':
  1466.                 continue
  1467.             ifs.append(name)
  1468.  
  1469.         ifs_file.close()
  1470.         return ifs
  1471.  
  1472.  
  1473.     def configure_network(self):
  1474.         """Automatically configure the network.
  1475.  
  1476.         At present, the only thing the user gets to tweak in the UI is the
  1477.         hostname. Some other things will be copied from the live filesystem,
  1478.         so changes made there will be reflected in the installed system.
  1479.  
  1480.         Unfortunately, at present we have to duplicate a fair bit of netcfg
  1481.         here, because it's hard to drive netcfg in a way that won't try to
  1482.         bring interfaces up and down."""
  1483.  
  1484.         # TODO cjwatson 2006-03-30: just call netcfg instead of doing all
  1485.         # this; requires a netcfg binary that doesn't bring interfaces up
  1486.         # and down
  1487.  
  1488.         for path in ('/etc/network/interfaces', '/etc/resolv.conf'):
  1489.             if os.path.exists(path):
  1490.                 shutil.copy2(path, os.path.join(self.target, path[1:]))
  1491.  
  1492.         try:
  1493.             hostname = self.db.get('netcfg/get_hostname')
  1494.         except debconf.DebconfError:
  1495.             hostname = ''
  1496.         try:
  1497.             domain = self.db.get('netcfg/get_domain')
  1498.         except debconf.DebconfError:
  1499.             domain = ''
  1500.         if hostname == '':
  1501.             hostname = 'ubuntu'
  1502.  
  1503.         fp = open(os.path.join(self.target, 'etc/hostname'), 'w')
  1504.         print >>fp, hostname
  1505.         fp.close()
  1506.  
  1507.         hosts = open(os.path.join(self.target, 'etc/hosts'), 'w')
  1508.         print >>hosts, "127.0.0.1\tlocalhost"
  1509.         if domain:
  1510.             print >>hosts, "127.0.1.1\t%s.%s\t%s" % (hostname, domain,
  1511.                                                      hostname)
  1512.         else:
  1513.             print >>hosts, "127.0.1.1\t%s" % hostname
  1514.         print >>hosts, textwrap.dedent("""\
  1515.  
  1516.             # The following lines are desirable for IPv6 capable hosts
  1517.             ::1     ip6-localhost ip6-loopback
  1518.             fe00::0 ip6-localnet
  1519.             ff00::0 ip6-mcastprefix
  1520.             ff02::1 ip6-allnodes
  1521.             ff02::2 ip6-allrouters
  1522.             ff02::3 ip6-allhosts""")
  1523.         hosts.close()
  1524.  
  1525.         persistent_net = '/etc/udev/rules.d/70-persistent-net.rules'
  1526.         if os.path.exists(persistent_net):
  1527.             shutil.copy2(persistent_net,
  1528.                          os.path.join(self.target, persistent_net[1:]))
  1529.         else:
  1530.             # TODO cjwatson 2006-03-30: from <bits/ioctls.h>; ugh, but no
  1531.             # binding available
  1532.             SIOCGIFHWADDR = 0x8927
  1533.             # <net/if_arp.h>
  1534.             ARPHRD_ETHER = 1
  1535.  
  1536.             if_names = {}
  1537.             sock = socket.socket(socket.SOCK_DGRAM)
  1538.             interfaces = self.get_all_interfaces()
  1539.             for i in range(len(interfaces)):
  1540.                 if_names[interfaces[i]] = struct.unpack('H6s',
  1541.                     fcntl.ioctl(sock.fileno(), SIOCGIFHWADDR,
  1542.                                 struct.pack('256s', interfaces[i]))[16:24])
  1543.             sock.close()
  1544.  
  1545.             iftab = open(os.path.join(self.target, 'etc/iftab'), 'w')
  1546.  
  1547.             print >>iftab, textwrap.dedent("""\
  1548.                 # This file assigns persistent names to network interfaces.
  1549.                 # See iftab(5) for syntax.
  1550.                 """)
  1551.  
  1552.             for i in range(len(interfaces)):
  1553.                 dup = False
  1554.                 with_arp = False
  1555.  
  1556.                 if_name = if_names[interfaces[i]]
  1557.                 if if_name is None or if_name[0] != ARPHRD_ETHER:
  1558.                     continue
  1559.  
  1560.                 for j in range(len(interfaces)):
  1561.                     if i == j or if_names[interfaces[j]] is None:
  1562.                         continue
  1563.                     if if_name[1] != if_names[interfaces[j]][1]:
  1564.                         continue
  1565.  
  1566.                     if if_names[interfaces[j]][0] == ARPHRD_ETHER:
  1567.                         dup = True
  1568.  
  1569.                 if dup:
  1570.                     continue
  1571.  
  1572.                 line = (interfaces[i] + " mac " +
  1573.                         ':'.join(['%02x' % ord(if_name[1][c])
  1574.                                   for c in range(6)]))
  1575.                 line += " arp %d" % if_name[0]
  1576.                 print >>iftab, line
  1577.  
  1578.             iftab.close()
  1579.  
  1580.  
  1581.     def configure_bootloader(self):
  1582.         """configuring and installing boot loader into installed
  1583.         hardware system."""
  1584.         install_bootloader = self.db.get('ubiquity/install_bootloader')
  1585.         if install_bootloader == "true":
  1586.             misc.execute('mount', '--bind', '/proc', self.target + '/proc')
  1587.             misc.execute('mount', '--bind', '/dev', self.target + '/dev')
  1588.  
  1589.             archdetect = subprocess.Popen(['archdetect'], stdout=subprocess.PIPE)
  1590.             subarch = archdetect.communicate()[0].strip()
  1591.  
  1592.             try:
  1593.                 if subarch.startswith('amd64/') or subarch.startswith('i386/') or subarch.startswith('lpia/'):
  1594.                     from ubiquity.components import grubinstaller
  1595.                     dbfilter = grubinstaller.GrubInstaller(None)
  1596.                     ret = dbfilter.run_command(auto_process=True)
  1597.                     if ret != 0:
  1598.                         raise InstallStepError(
  1599.                             "GrubInstaller failed with code %d" % ret)
  1600.                 elif subarch == 'powerpc/ps3':
  1601.                     from ubiquity.components import kbootinstaller
  1602.                     dbfilter = kbootinstaller.KbootInstaller(None)
  1603.                     ret = dbfilter.run_command(auto_process=True)
  1604.                     if ret != 0:
  1605.                         raise InstallStepError(
  1606.                             "KbootInstaller failed with code %d" % ret)
  1607.                 elif subarch.startswith('powerpc/'):
  1608.                     from ubiquity.components import yabootinstaller
  1609.                     dbfilter = yabootinstaller.YabootInstaller(None)
  1610.                     ret = dbfilter.run_command(auto_process=True)
  1611.                     if ret != 0:
  1612.                         raise InstallStepError(
  1613.                             "YabootInstaller failed with code %d" % ret)
  1614.                 else:
  1615.                     raise InstallStepError("No bootloader installer found")
  1616.             except ImportError:
  1617.                 raise InstallStepError("No bootloader installer found")
  1618.  
  1619.             misc.execute('umount', '-f', self.target + '/proc')
  1620.             misc.execute('umount', '-f', self.target + '/dev')
  1621.  
  1622.  
  1623.     def broken_packages(self, cache):
  1624.         brokenpkgs = set()
  1625.         for pkg in cache.keys():
  1626.             try:
  1627.                 if cache._depcache.IsInstBroken(cache._cache[pkg]):
  1628.                     brokenpkgs.add(pkg)
  1629.             except KeyError:
  1630.                 # Apparently sometimes the cache goes a bit bonkers ...
  1631.                 continue
  1632.         return brokenpkgs
  1633.  
  1634.     def do_install(self, to_install):
  1635.         if self.langpacks:
  1636.             self.db.progress('START', 0, 10, 'ubiquity/langpacks/title')
  1637.         else:
  1638.             self.db.progress('START', 0, 10, 'ubiquity/install/title')
  1639.         self.db.progress('INFO', 'ubiquity/install/find_installables')
  1640.  
  1641.         self.db.progress('REGION', 0, 1)
  1642.         fetchprogress = DebconfFetchProgress(
  1643.             self.db, 'ubiquity/install/title',
  1644.             'ubiquity/install/apt_indices_starting',
  1645.             'ubiquity/install/apt_indices')
  1646.         cache = Cache()
  1647.  
  1648.         if cache._depcache.BrokenCount > 0:
  1649.             syslog.syslog(
  1650.                 'not installing additional packages, since there are broken '
  1651.                 'packages: %s' % ', '.join(self.broken_packages(cache)))
  1652.             self.db.progress('STOP')
  1653.             return
  1654.  
  1655.         for pkg in to_install:
  1656.             self.mark_install(cache, pkg)
  1657.  
  1658.         self.db.progress('SET', 1)
  1659.         self.db.progress('REGION', 1, 10)
  1660.         if self.langpacks:
  1661.             fetchprogress = DebconfFetchProgress(
  1662.                 self.db, 'ubiquity/langpacks/title', None,
  1663.                 'ubiquity/langpacks/packages')
  1664.             installprogress = DebconfInstallProgress(
  1665.                 self.db, 'ubiquity/langpacks/title',
  1666.                 'ubiquity/install/apt_info')
  1667.         else:
  1668.             fetchprogress = DebconfFetchProgress(
  1669.                 self.db, 'ubiquity/install/title', None,
  1670.                 'ubiquity/install/fetch_remove')
  1671.             installprogress = DebconfInstallProgress(
  1672.                 self.db, 'ubiquity/install/title',
  1673.                 'ubiquity/install/apt_info',
  1674.                 'ubiquity/install/apt_error_install')
  1675.         self.chroot_setup()
  1676.         commit_error = None
  1677.         try:
  1678.             try:
  1679.                 if not cache.commit(fetchprogress, installprogress):
  1680.                     fetchprogress.stop()
  1681.                     installprogress.finishUpdate()
  1682.                     self.db.progress('STOP')
  1683.                     return
  1684.             except IOError, e:
  1685.                 for line in str(e).split('\n'):
  1686.                     syslog.syslog(syslog.LOG_ERR, line)
  1687.                 fetchprogress.stop()
  1688.                 installprogress.finishUpdate()
  1689.                 self.db.progress('STOP')
  1690.                 return
  1691.             except SystemError, e:
  1692.                 for line in str(e).split('\n'):
  1693.                     syslog.syslog(syslog.LOG_ERR, line)
  1694.                 commit_error = str(e)
  1695.         finally:
  1696.             self.chroot_cleanup()
  1697.         self.db.progress('SET', 10)
  1698.  
  1699.         cache.open(None)
  1700.         if commit_error or cache._depcache.BrokenCount > 0:
  1701.             if commit_error is None:
  1702.                 commit_error = ''
  1703.             brokenpkgs = self.broken_packages(cache)
  1704.             syslog.syslog('broken packages after installation: '
  1705.                           '%s' % ', '.join(brokenpkgs))
  1706.             self.db.subst('ubiquity/install/broken_install', 'ERROR',
  1707.                           commit_error)
  1708.             self.db.subst('ubiquity/install/broken_install', 'PACKAGES',
  1709.                           ', '.join(brokenpkgs))
  1710.             self.db.input('critical', 'ubiquity/install/broken_install')
  1711.             self.db.go()
  1712.  
  1713.         self.db.progress('STOP')
  1714.  
  1715.  
  1716.     def get_remove_list(self, cache, to_remove, recursive=False):
  1717.         to_remove = set(to_remove)
  1718.         all_removed = set()
  1719.         while True:
  1720.             removed = set()
  1721.             for pkg in to_remove:
  1722.                 cachedpkg = self.get_cache_pkg(cache, pkg)
  1723.                 if cachedpkg is not None and cachedpkg.isInstalled:
  1724.                     apt_error = False
  1725.                     try:
  1726.                         cachedpkg.markDelete(autoFix=False, purge=True)
  1727.                     except SystemError:
  1728.                         apt_error = True
  1729.                     if apt_error:
  1730.                         cachedpkg.markKeep()
  1731.                     elif cache._depcache.BrokenCount > 0:
  1732.                         # If we're recursively removing packages, or if all
  1733.                         # of the broken packages are in the set of packages
  1734.                         # to remove anyway, then go ahead and try to remove
  1735.                         # them too.
  1736.                         brokenpkgs = self.broken_packages(cache)
  1737.                         broken_removed = set()
  1738.                         while brokenpkgs and (recursive or
  1739.                                               brokenpkgs <= to_remove):
  1740.                             broken_removed_inner = set()
  1741.                             for pkg2 in brokenpkgs:
  1742.                                 cachedpkg2 = self.get_cache_pkg(cache, pkg2)
  1743.                                 if cachedpkg2 is not None:
  1744.                                     broken_removed_inner.add(pkg2)
  1745.                                     try:
  1746.                                         cachedpkg2.markDelete(autoFix=False,
  1747.                                                               purge=True)
  1748.                                     except SystemError:
  1749.                                         apt_error = True
  1750.                                         break
  1751.                             broken_removed |= broken_removed_inner
  1752.                             if apt_error or not broken_removed_inner:
  1753.                                 break
  1754.                             brokenpkgs = self.broken_packages(cache)
  1755.                         if apt_error or cache._depcache.BrokenCount > 0:
  1756.                             # That didn't work. Revert all the removals we
  1757.                             # just tried.
  1758.                             for pkg2 in broken_removed:
  1759.                                 self.get_cache_pkg(cache, pkg2).markKeep()
  1760.                             cachedpkg.markKeep()
  1761.                         else:
  1762.                             removed.add(pkg)
  1763.                             removed |= broken_removed
  1764.                     else:
  1765.                         removed.add(pkg)
  1766.                     assert cache._depcache.BrokenCount == 0
  1767.             if not removed:
  1768.                 break
  1769.             to_remove -= removed
  1770.             all_removed |= removed
  1771.         return all_removed
  1772.  
  1773.  
  1774.     def do_remove(self, to_remove, recursive=False):
  1775.         self.db.progress('START', 0, 5, 'ubiquity/install/title')
  1776.         self.db.progress('INFO', 'ubiquity/install/find_removables')
  1777.  
  1778.         fetchprogress = DebconfFetchProgress(
  1779.             self.db, 'ubiquity/install/title',
  1780.             'ubiquity/install/apt_indices_starting',
  1781.             'ubiquity/install/apt_indices')
  1782.         cache = Cache()
  1783.  
  1784.         if cache._depcache.BrokenCount > 0:
  1785.             syslog.syslog(
  1786.                 'not processing removals, since there are broken packages: '
  1787.                 '%s' % ', '.join(self.broken_packages(cache)))
  1788.             self.db.progress('STOP')
  1789.             return
  1790.  
  1791.         self.get_remove_list(cache, to_remove, recursive)
  1792.  
  1793.         self.db.progress('SET', 1)
  1794.         self.db.progress('REGION', 1, 5)
  1795.         fetchprogress = DebconfFetchProgress(
  1796.             self.db, 'ubiquity/install/title', None,
  1797.             'ubiquity/install/fetch_remove')
  1798.         installprogress = DebconfInstallProgress(
  1799.             self.db, 'ubiquity/install/title', 'ubiquity/install/apt_info',
  1800.             'ubiquity/install/apt_error_remove')
  1801.         self.chroot_setup()
  1802.         commit_error = None
  1803.         try:
  1804.             try:
  1805.                 if not cache.commit(fetchprogress, installprogress):
  1806.                     fetchprogress.stop()
  1807.                     installprogress.finishUpdate()
  1808.                     self.db.progress('STOP')
  1809.                     return
  1810.             except SystemError, e:
  1811.                 for line in str(e).split('\n'):
  1812.                     syslog.syslog(syslog.LOG_ERR, line)
  1813.                 commit_error = str(e)
  1814.         finally:
  1815.             self.chroot_cleanup()
  1816.         self.db.progress('SET', 5)
  1817.  
  1818.         cache.open(None)
  1819.         if commit_error or cache._depcache.BrokenCount > 0:
  1820.             if commit_error is None:
  1821.                 commit_error = ''
  1822.             brokenpkgs = self.broken_packages(cache)
  1823.             syslog.syslog('broken packages after removal: '
  1824.                           '%s' % ', '.join(brokenpkgs))
  1825.             self.db.subst('ubiquity/install/broken_remove', 'ERROR',
  1826.                           commit_error)
  1827.             self.db.subst('ubiquity/install/broken_remove', 'PACKAGES',
  1828.                           ', '.join(brokenpkgs))
  1829.             self.db.input('critical', 'ubiquity/install/broken_remove')
  1830.             self.db.go()
  1831.  
  1832.         self.db.progress('STOP')
  1833.  
  1834.  
  1835.     def remove_unusable_kernels(self):
  1836.         """Remove unusable kernels; keeping them may cause us to be unable
  1837.         to boot."""
  1838.  
  1839.         self.db.progress('START', 0, 5, 'ubiquity/install/title')
  1840.  
  1841.         self.db.progress('INFO', 'ubiquity/install/find_removables')
  1842.  
  1843.         # Check for kernel packages to remove.
  1844.         dbfilter = check_kernels.CheckKernels(None)
  1845.         dbfilter.run_command(auto_process=True)
  1846.  
  1847.         remove_kernels = set()
  1848.         if os.path.exists("/var/lib/ubiquity/remove-kernels"):
  1849.             remove_kernels_file = open("/var/lib/ubiquity/remove-kernels")
  1850.             for line in remove_kernels_file:
  1851.                 remove_kernels.add(line.strip())
  1852.             remove_kernels_file.close()
  1853.  
  1854.         if len(remove_kernels) == 0:
  1855.             self.db.progress('STOP')
  1856.             return
  1857.  
  1858.         self.db.progress('SET', 1)
  1859.         self.db.progress('REGION', 1, 5)
  1860.         try:
  1861.             self.do_remove(remove_kernels, recursive=True)
  1862.         except:
  1863.             self.db.progress('STOP')
  1864.             raise
  1865.         self.db.progress('SET', 5)
  1866.         self.db.progress('STOP')
  1867.  
  1868.  
  1869.     def install_extras(self):
  1870.         """Try to install additional packages requested by installer
  1871.         components."""
  1872.  
  1873.         # We only ever install these packages from the CD.
  1874.         sources_list = os.path.join(self.target, 'etc/apt/sources.list')
  1875.         os.rename(sources_list, "%s.apt-setup" % sources_list)
  1876.         old_sources = open("%s.apt-setup" % sources_list)
  1877.         new_sources = open(sources_list, 'w')
  1878.         found_cdrom = False
  1879.         for line in old_sources:
  1880.             if 'cdrom:' in line:
  1881.                 print >>new_sources, line,
  1882.                 found_cdrom = True
  1883.         new_sources.close()
  1884.         old_sources.close()
  1885.         if not found_cdrom:
  1886.             os.rename("%s.apt-setup" % sources_list, sources_list)
  1887.  
  1888.         self.do_install(self.query_recorded_installed())
  1889.  
  1890.         if found_cdrom:
  1891.             os.rename("%s.apt-setup" % sources_list, sources_list)
  1892.  
  1893.         # TODO cjwatson 2007-08-09: python reimplementation of
  1894.         # oem-config/finish-install.d/07oem-config-user. This really needs
  1895.         # to die in a great big chemical fire and call the same shell script
  1896.         # instead.
  1897.         try:
  1898.             if self.db.get('oem-config/enable') == 'true':
  1899.                 if os.path.isdir(os.path.join(self.target, 'home/oem')):
  1900.                     open(os.path.join(self.target, 'home/oem/.hwdb'),
  1901.                          'w').close()
  1902.  
  1903.                     for desktop_file in (
  1904.                         'usr/share/applications/oem-config-prepare-gtk.desktop',
  1905.                         'usr/share/applications/kde/oem-config-prepare-kde.desktop'):
  1906.                         if os.path.exists(os.path.join(self.target,
  1907.                                                        desktop_file)):
  1908.                             desktop_base = os.path.basename(desktop_file)
  1909.                             self.chrex('install', '-d',
  1910.                                        '-o', 'oem', '-g', 'oem',
  1911.                                        '/home/oem/Desktop')
  1912.                             self.chrex('install', '-o', 'oem', '-g', 'oem',
  1913.                                        '/%s' % desktop_file,
  1914.                                        '/home/oem/Desktop/%s' % desktop_base)
  1915.                             break
  1916.  
  1917.                 # Some serious horribleness is needed here to handle absolute
  1918.                 # symlinks.
  1919.                 for name in ('gdm-cdd.conf', 'gdm.conf'):
  1920.                     gdm_conf = osextras.realpath_root(
  1921.                         self.target, os.path.join('/etc/gdm', name))
  1922.                     if os.path.isfile(gdm_conf):
  1923.                         self.chrex('sed', '-i.oem',
  1924.                                    '-e', 's/^AutomaticLoginEnable=.*$/AutomaticLoginEnable=true/',
  1925.                                    '-e', 's/^AutomaticLogin=.*$/AutomaticLogin=oem/',
  1926.                                    '-e', 's/^TimedLoginEnable=.*$/TimedLoginEnable=true/',
  1927.                                    '-e', 's/^TimedLogin=.*$/TimedLogin=oem/',
  1928.                                    '-e', 's/^TimedLoginDelay=.*$/TimedLoginDelay=10/',
  1929.                                    os.path.join('/etc/gdm', name));
  1930.                         break
  1931.  
  1932.                 kdmrc = os.path.join(self.target, 'etc/kde4/kdm/kdmrc')
  1933.                 if os.path.isfile(kdmrc):
  1934.                     misc.execute('sed', '-i.oem', '-r',
  1935.                                  '-e', 's/^#?AutoLoginEnable=.*$/AutoLoginEnable=true/',
  1936.                                  '-e', 's/^#?AutoLoginUser=.*$/AutoLoginUser=oem/',
  1937.                                  '-e', 's/^#?AutoReLogin=.*$/AutoReLogin=true/',
  1938.                                  kdmrc)
  1939.  
  1940.         # Carry the locale setting over to the installed system.
  1941.         # This mimics the behavior in 01oem-config-udeb.
  1942.                 di_locale = self.db.get('debian-installer/locale')
  1943.                 if di_locale:
  1944.                     self.set_debconf('debian-installer/locale', di_locale)
  1945.         except debconf.DebconfError:
  1946.             pass
  1947.  
  1948.         # Tell apt-install to install packages directly from now on.
  1949.         apt_install_direct = open('/var/lib/ubiquity/apt-install-direct', 'w')
  1950.         apt_install_direct.close()
  1951.  
  1952.  
  1953.     def remove_extras(self):
  1954.         """Try to remove packages that are needed on the live CD but not on
  1955.         the installed system."""
  1956.  
  1957.         # Looking through files for packages to remove is pretty quick, so
  1958.         # don't bother with a progress bar for that.
  1959.  
  1960.         # Check for packages specific to the live CD.
  1961.         if (os.path.exists("/cdrom/casper/filesystem.manifest-desktop") and
  1962.             os.path.exists("/cdrom/casper/filesystem.manifest")):
  1963.             desktop_packages = set()
  1964.             manifest = open("/cdrom/casper/filesystem.manifest-desktop")
  1965.             for line in manifest:
  1966.                 if line.strip() != '' and not line.startswith('#'):
  1967.                     desktop_packages.add(line.split()[0])
  1968.             manifest.close()
  1969.             live_packages = set()
  1970.             manifest = open("/cdrom/casper/filesystem.manifest")
  1971.             for line in manifest:
  1972.                 if line.strip() != '' and not line.startswith('#'):
  1973.                     live_packages.add(line.split()[0])
  1974.             manifest.close()
  1975.             difference = live_packages - desktop_packages
  1976.         else:
  1977.             difference = set()
  1978.  
  1979.         # Keep packages we explicitly installed.
  1980.         difference -= self.query_recorded_installed()
  1981.  
  1982.         if len(difference) == 0:
  1983.             return
  1984.  
  1985.         use_restricted = True
  1986.         try:
  1987.             if self.db.get('apt-setup/restricted') == 'false':
  1988.                 use_restricted = False
  1989.         except debconf.DebconfError:
  1990.             pass
  1991.         if not use_restricted:
  1992.             cache = Cache()
  1993.             for pkg in cache.keys():
  1994.                 if (cache[pkg].isInstalled and
  1995.                     cache[pkg].section.startswith('restricted/')):
  1996.                     difference.add(pkg)
  1997.             del cache
  1998.  
  1999.         # Don't worry about failures removing packages; it will be easier
  2000.         # for the user to sort them out with a graphical package manager (or
  2001.         # whatever) after installation than it will be to try to deal with
  2002.         # them automatically here.
  2003.         self.do_remove(difference)
  2004.  
  2005.     def remove_broken_cdrom(self):
  2006.         fstab = os.path.join(self.target, 'etc/fstab')
  2007.         ret = []
  2008.         try:
  2009.             fp = open(fstab)
  2010.             for line in fp:
  2011.                 l = line.split()
  2012.                 if len(l) > 2:
  2013.                     if l[1].startswith('/cdrom') or l[1].startswith('/media/cdrom'):
  2014.                         try:
  2015.                             fstype = subprocess.Popen(
  2016.                                 ['vol_id', '--type', l[0]],
  2017.                                 stdout=subprocess.PIPE).communicate()[0].rstrip('\n')
  2018.                             if fstype != 'iso9660' and fstype != 'udf':
  2019.                                 continue
  2020.                         except OSError:
  2021.                             pass
  2022.                 ret.append(line)
  2023.             fp.close()
  2024.             fp = open(fstab, 'w')
  2025.             fp.writelines(ret)
  2026.         except Exception, e:
  2027.             syslog.syslog(syslog.LOG_ERR, 'Exception during installation:')
  2028.             syslog.syslog(syslog.LOG_ERR,
  2029.                 'Unable to process /etc/fstab: ' + str(e))
  2030.         finally:
  2031.             if fp:
  2032.                 fp.close()
  2033.             
  2034.     def cleanup(self):
  2035.         """Miscellaneous cleanup tasks."""
  2036.  
  2037.         misc.execute('umount', os.path.join(self.target, 'cdrom'))
  2038.  
  2039.         env = dict(os.environ)
  2040.         env['OVERRIDE_BASE_INSTALLABLE'] = '1'
  2041.         subprocess.call(['/usr/lib/ubiquity/apt-setup/finish-install'],
  2042.                         env=env)
  2043.  
  2044.         for apt_conf in ('00NoMountCDROM', '00IgnoreTimeConflict',
  2045.                          '00AllowUnauthenticated'):
  2046.             try:
  2047.                 os.unlink(os.path.join(
  2048.                     self.target, 'etc/apt/apt.conf.d', apt_conf))
  2049.             except:
  2050.                 pass
  2051.  
  2052.         if self.source == '/var/lib/ubiquity/source':
  2053.             self.umount_source()
  2054.  
  2055.  
  2056.     def chrex(self, *args):
  2057.         """executes commands on chroot system (provided by *args)."""
  2058.         return misc.execute('chroot', self.target, *args)
  2059.  
  2060.  
  2061.     def copy_debconf(self, package):
  2062.         """setting debconf database into installed system."""
  2063.  
  2064.         # TODO cjwatson 2006-02-25: unusable here now because we have a
  2065.         # running debconf frontend that's locked the database; fortunately
  2066.         # this isn't critical. We still need to think about how to handle
  2067.         # preseeding in general, though.
  2068.         targetdb = os.path.join(self.target, 'var/cache/debconf/config.dat')
  2069.  
  2070.         misc.execute('debconf-copydb', 'configdb', 'targetdb', '-p',
  2071.                      '^%s/' % package, '--config=Name:targetdb',
  2072.                      '--config=Driver:File','--config=Filename:' + targetdb)
  2073.  
  2074.  
  2075.     def set_debconf(self, question, value):
  2076.         dccomm = subprocess.Popen(['log-output', '-t', 'ubiquity',
  2077.                                    '--pass-stdout',
  2078.                                    'chroot', self.target,
  2079.                                    'debconf-communicate',
  2080.                                    '-fnoninteractive', 'ubiquity'],
  2081.                                   stdin=subprocess.PIPE,
  2082.                                   stdout=subprocess.PIPE, close_fds=True)
  2083.         try:
  2084.             dc = debconf.Debconf(read=dccomm.stdout, write=dccomm.stdin)
  2085.             dc.set(question, value)
  2086.             dc.fset(question, 'seen', 'true')
  2087.         finally:
  2088.             dccomm.stdin.close()
  2089.             dccomm.wait()
  2090.  
  2091.  
  2092.     def reconfigure_preexec(self):
  2093.         debconf_disconnect()
  2094.         os.environ['XAUTHORITY'] = '/root/.Xauthority'
  2095.  
  2096.     def reconfigure(self, package):
  2097.         """executes a dpkg-reconfigure into installed system to each
  2098.         package which provided by args."""
  2099.         subprocess.call(['log-output', '-t', 'ubiquity', 'chroot', self.target,
  2100.                          'dpkg-reconfigure', '-fnoninteractive', package],
  2101.                         preexec_fn=self.reconfigure_preexec, close_fds=True)
  2102.  
  2103.  
  2104. if __name__ == '__main__':
  2105.     if not os.path.exists('/var/lib/ubiquity'):
  2106.         os.makedirs('/var/lib/ubiquity')
  2107.     if os.path.exists('/var/lib/ubiquity/install.trace'):
  2108.         os.unlink('/var/lib/ubiquity/install.trace')
  2109.  
  2110.     install = Install()
  2111.     sys.excepthook = install.excepthook
  2112.     install.run()
  2113.     sys.exit(0)
  2114.  
  2115. # vim:ai:et:sts=4:tw=80:sw=4:
  2116.